package com.meiya.hugegraph.api;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.hugegraph.driver.GraphManager;
import org.apache.hugegraph.driver.HugeClient;
import org.apache.hugegraph.driver.SchemaManager;
import org.apache.hugegraph.structure.constant.Direction;
import org.apache.hugegraph.structure.constant.T;
import org.apache.hugegraph.structure.graph.Edge;
import org.apache.hugegraph.structure.graph.Vertex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.meiya.hugegraph.config.HugeGraphConfig;

/**
 * HugeGraph图管理类
 * 集成了顶点和边的各种操作，提供完整的图操作接口
 */
@Component
public class HugeGraphManager {
    
    private static final Logger logger = LoggerFactory.getLogger(HugeGraphManager.class);
    private final HugeClient client;
    private final GraphManager graphManager;
    private final SchemaManager schemaManager;
    
    /**
     * 构造函数，使用注入的配置创建HugeClient实例
     *
     * @param config HugeGraph配置
     */
    @Autowired
    public HugeGraphManager(HugeGraphConfig config) {
        this.client = HugeGraphClientFactory.createClient(
            config.getServerUrl(), 
            config.getGraphName(), 
            config.getTimeout()
        );
        this.graphManager = client.graph();
        this.schemaManager = client.schema();
    }
    
    /**
     * 获取HugeClient实例
     *
     * @return HugeClient实例
     */
    public HugeClient getClient() {
        return client;
    }
    
    /**
     * 获取GraphManager实例
     *
     * @return GraphManager实例
     */
    public GraphManager getGraphManager() {
        return graphManager;
    }
    
    /**
     * 获取SchemaManager实例
     *
     * @return SchemaManager实例
     */
    public SchemaManager getSchemaManager() {
        return schemaManager;
    }
    
    /**
     * 关闭HugeClient连接
     */
    public void close() {
        HugeGraphClientFactory.closeClient(this.client);
    }
    
    // ========================= 顶点操作方法 =========================
    
    /**
     * 创建一个顶点
     *
     * @param label      顶点标签
     * @param properties 顶点属性
     * @return 创建的顶点
     */
    public Vertex addVertex(String label, Map<String, Object> properties) {
        if (label == null || label.isEmpty()) {
            throw new IllegalArgumentException("顶点标签不能为空");
        }
        
        logger.info("创建顶点，标签: {}，属性: {}", label, properties);
        
        try {
            // 构建属性数组
            Object[] kvs = new Object[properties.size() * 2 + 2];
            kvs[0] = T.LABEL;
            kvs[1] = label;
            
            int i = 2;
            for (Map.Entry<String, Object> entry : properties.entrySet()) {
                kvs[i++] = entry.getKey();
                kvs[i++] = entry.getValue();
            }
            
            Vertex vertex = this.graphManager.addVertex(kvs);
            logger.info("顶点创建成功，ID: {}", vertex.id());
            return vertex;
        } catch (Exception e) {
            logger.error("创建顶点失败", e);
            throw new RuntimeException("创建顶点失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 创建"card"类型的顶点（卡）
     *
     * @param cardNumber 卡号
     * @param holderName 持卡人姓名
     * @return 创建的顶点
     */
    public Vertex addCard(String cardNumber, String holderName) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("cardNumber", cardNumber);
        properties.put("holderName", holderName);
        
        return addVertex("card", properties);
    }
    
    /**
     * 批量创建顶点
     *
     * @param vertices 顶点列表，每个顶点用Map表示，必须包含"label"和"properties"两个键
     * @return 创建的顶点ID列表
     */
    public List<Object> addVertices(List<Map<String, Object>> vertices) {
        if (vertices == null || vertices.isEmpty()) {
            return new ArrayList<>();
        }
        
        logger.info("批量创建顶点，数量: {}", vertices.size());
        List<Object> ids = new ArrayList<>();
        
        try {
            for (Map<String, Object> vertexMap : vertices) {
                String label = (String) vertexMap.get("label");
                @SuppressWarnings("unchecked")
                Map<String, Object> properties = (Map<String, Object>) vertexMap.get("properties");
                
                if (label == null || properties == null) {
                    logger.warn("跳过无效顶点数据: {}", vertexMap);
                    continue;
                }
                
                Vertex vertex = addVertex(label, properties);
                ids.add(vertex.id());
            }
            logger.info("批量创建顶点完成，成功创建: {}", ids.size());
            return ids;
        } catch (Exception e) {
            logger.error("批量创建顶点失败", e);
            throw new RuntimeException("批量创建顶点失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 根据ID获取顶点
     *
     * @param id 顶点ID
     * @return 顶点对象，如果不存在则返回null
     */
    public Vertex getVertex(Object id) {
        if (id == null) {
            throw new IllegalArgumentException("顶点ID不能为空");
        }
        
        logger.info("根据ID获取顶点: {}", id);
        
        try {
            return this.graphManager.getVertex(id);
        } catch (Exception e) {
            logger.error("获取顶点失败: {}", id, e);
            return null;
        }
    }
    
    /**
     * 查询顶点列表
     *
     * @param label      顶点标签，可以为null
     * @param properties 属性条件，可以为null
     * @param limit      限制结果数量，小于等于0表示不限制
     * @return 符合条件的顶点列表
     */
    public List<Vertex> listVertices(String label, Map<String, Object> properties, int limit) {
        logger.info("查询顶点，标签: {}，属性条件: {}，限制数量: {}", label, properties, limit);
        
        try {
            List<Vertex> vertices;
            
            if (label == null && (properties == null || properties.isEmpty())) {
                // 无条件查询所有顶点
                if (limit > 0) {
                    vertices = this.graphManager.listVertices(limit);
                } else {
                    vertices = this.graphManager.listVertices();
                }
            } else if (label != null && (properties == null || properties.isEmpty())) {
                // 仅按标签查询
                if (limit > 0) {
                    vertices = this.graphManager.listVertices(label, limit);
                } else {
                    vertices = this.graphManager.listVertices(label);
                }
            } else {
                // 按属性条件查询
                Map<String, Object> params = new HashMap<>(properties);

                if (limit > 0) {
                    vertices = this.graphManager.listVertices(label, params, limit);
                } else {
                    vertices = this.graphManager.listVertices(label, params);
                }
            }

            List<Vertex> results = new ArrayList<>(vertices);
            
            logger.info("查询顶点结果数量: {}", results.size());
            return results;
        } catch (Exception e) {
            logger.error("查询顶点失败", e);
            throw new RuntimeException("查询顶点失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 删除顶点
     *
     * @param id 顶点ID
     * @return 是否删除成功
     */
    public boolean removeVertex(Object id) {
        if (id == null) {
            throw new IllegalArgumentException("顶点ID不能为空");
        }
        
        logger.info("删除顶点，ID: {}", id);
        
        try {
            this.graphManager.removeVertex(id);
            logger.info("顶点删除成功: {}", id);
            return true;
        } catch (Exception e) {
            logger.error("删除顶点失败: {}", id, e);
            return false;
        }
    }
    
    // ========================= 边操作方法 =========================
    
    /**
     * 添加边
     *
     * @param sourceId   源顶点ID
     * @param targetId   目标顶点ID
     * @param label      边标签
     * @param properties 边属性
     * @return 创建的边
     */
    public Edge addEdge(Object sourceId, Object targetId, String label, Map<String, Object> properties) {
        if (sourceId == null || targetId == null) {
            throw new IllegalArgumentException("源顶点ID和目标顶点ID不能为空");
        }
        
        if (label == null || label.isEmpty()) {
            throw new IllegalArgumentException("边标签不能为空");
        }
        
        logger.info("创建边，从 {} 到 {}，标签: {}，属性: {}", sourceId, targetId, label, properties);
        
        try {
            // 获取源顶点和目标顶点
            Vertex sourceVertex = getVertex(sourceId);
            Vertex targetVertex = getVertex(targetId);
            
            if (sourceVertex == null) {
                throw new IllegalArgumentException("源顶点不存在: " + sourceId);
            }
            
            if (targetVertex == null) {
                throw new IllegalArgumentException("目标顶点不存在: " + targetId);
            }
            
            // 创建边的参数数组
            List<Object> params = new ArrayList<>();
            for (Map.Entry<String, Object> entry : properties.entrySet()) {
                params.add(entry.getKey());
                params.add(entry.getValue());
            }
            
            // 使用数组转换工具方法
            Object[] kvs = params.toArray(new Object[0]);
            
            // 创建边
            Edge edge = sourceVertex.addEdge(label, targetVertex, kvs);
            logger.info("边创建成功，ID: {}", edge.id());
            return edge;
        } catch (Exception e) {
            logger.error("创建边失败", e);
            throw new RuntimeException("创建边失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 创建交易边
     *
     * @param sourceCardId 源卡ID
     * @param targetCardId 目标卡ID
     * @param amount       交易金额
     * @param transTime    交易时间
     * @param ukey         交易唯一标识
     * @return 创建的边
     */
    public Edge addTransaction(Object sourceCardId, Object targetCardId, 
                               double amount, Date transTime, String ukey) {
        Map<String, Object> properties = new HashMap<>();
        properties.put("amount", amount);
        properties.put("transTime", transTime);
        properties.put("ukey", ukey);
        
        return addEdge(sourceCardId, targetCardId, "transaction", properties);
    }
    
    /**
     * 查询边列表
     *
     * @param label      边标签，可以为null
     * @param properties 属性条件，可以为null
     * @param limit      限制结果数量，小于等于0表示不限制
     * @return 符合条件的边列表
     */
    public List<Edge> listEdges(String label, Map<String, Object> properties, int limit) {
        logger.info("查询边，标签: {}，属性条件: {}，限制数量: {}", label, properties, limit);
        
        try {
            List<Edge> edges;
            
            if (label == null && (properties == null || properties.isEmpty())) {
                // 无条件查询所有边
                if (limit > 0) {
                    edges = this.graphManager.listEdges(limit);
                } else {
                    edges = this.graphManager.listEdges();
                }
            } else if (label != null && (properties == null || properties.isEmpty())) {
                // 仅按标签查询
                if (limit > 0) {
                    edges = this.graphManager.listEdges(label, limit);
                } else {
                    edges = this.graphManager.listEdges(label);
                }
            } else {
                // 按属性条件查询
                Map<String, Object> params = new HashMap<>(properties);

                if (limit > 0) {
                    edges = this.graphManager.listEdges(label, params, limit);
                } else {
                    edges = this.graphManager.listEdges(label, params);
                }
            }

            List<Edge> results = new ArrayList<>(edges);
            
            logger.info("查询边结果数量: {}", results.size());
            return results;
        } catch (Exception e) {
            logger.error("查询边失败", e);
            throw new RuntimeException("查询边失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 获取与指定顶点相连的边
     *
     * @param vertexId   顶点ID
     * @param direction  方向（null表示双向，OUT表示出边，IN表示入边）
     * @param labels     边标签列表，null表示所有标签
     * @param limit      限制返回结果数量
     * @return 相连的边列表
     */
    public List<Edge> getEdgesOfVertex(Object vertexId, Direction direction, List<String> labels, int limit) {
        if (vertexId == null) {
            throw new IllegalArgumentException("顶点ID不能为空");
        }
        
        logger.info("获取顶点{}的{}边，标签: {}，限制数量: {}", 
                   vertexId, 
                   direction == null ? "所有" : direction.toString(), 
                   labels, 
                   limit);
        
        try {
            // 获取顶点
            Vertex vertex = this.graphManager.getVertex(vertexId);
            if (vertex == null) {
                throw new IllegalArgumentException("顶点不存在: " + vertexId);
            }
            
            final Direction directionToUse = direction == null ? Direction.BOTH : direction;
            List<Edge> results = new ArrayList<>();
            
            // 根据标签获取边
            if (labels == null || labels.isEmpty()) {
                // 使用vertex_id作为查询条件
                List<Edge> allEdges = limit > 0 ? 
                    this.graphManager.listEdges(limit) : 
                    this.graphManager.listEdges();
                    
                // 过滤与指定顶点相关的边
                for (Edge edge : allEdges) {
                    if ((directionToUse == Direction.OUT || directionToUse == Direction.BOTH) && 
                        edge.sourceId().equals(vertex.id())) {
                        results.add(edge);
                    } else if ((directionToUse == Direction.IN || directionToUse == Direction.BOTH) && 
                               edge.targetId().equals(vertex.id())) {
                        results.add(edge);
                    }
                    
                    // 如果达到了限制数量，则结束
                    if (limit > 0 && results.size() >= limit) {
                        break;
                    }
                }
            } else {
                // 按边标签分别查询并过滤
                for (String label : labels) {
                    List<Edge> labelEdges = limit > 0 ? 
                        this.graphManager.listEdges(label, limit) : 
                        this.graphManager.listEdges(label);
                    
                    for (Edge edge : labelEdges) {
                        if ((directionToUse == Direction.OUT || directionToUse == Direction.BOTH) && 
                            edge.sourceId().equals(vertex.id())) {
                            results.add(edge);
                        } else if ((directionToUse == Direction.IN || directionToUse == Direction.BOTH) && 
                                   edge.targetId().equals(vertex.id())) {
                            results.add(edge);
                        }
                        
                        // 如果达到了限制数量，则结束
                        if (limit > 0 && results.size() >= limit) {
                            break;
                        }
                    }
                    
                    if (limit > 0 && results.size() >= limit) {
                        break;
                    }
                }
            }
            
            logger.info("获取到{}条边", results.size());
            return results;
        } catch (Exception e) {
            logger.error("获取顶点的边失败: {}", vertexId, e);
            throw new RuntimeException("获取顶点的边失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 删除边
     *
     * @param id 边ID
     * @return 是否删除成功
     */
    public boolean removeEdge(String id) {
        if (id == null) {
            throw new IllegalArgumentException("边ID不能为空");
        }
        
        logger.info("删除边，ID: {}", id);
        
        try {
            this.graphManager.removeEdge(id);
            logger.info("边删除成功: {}", id);
            return true;
        } catch (Exception e) {
            logger.error("删除边失败: {}", id, e);
            return false;
        }
    }
} 